package cn.iocoder.yudao.module.shop.service.order;

import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
import cn.iocoder.yudao.module.shop.config.shop;
import cn.iocoder.yudao.module.shop.controller.admin.item.vo.ShopItemRespVO;
import cn.iocoder.yudao.module.shop.dal.dataobject.crosspriceitem.ShopCrossPriceItemDO;
import cn.iocoder.yudao.module.shop.dal.dataobject.item.ShopItemDO;

import cn.iocoder.yudao.module.shop.dal.dataobject.itemcount.ShopItemCountDO;
import cn.iocoder.yudao.module.shop.dal.dataobject.itemtimes.ShopItemTimesDO;
import cn.iocoder.yudao.module.shop.dal.dataobject.memberpay.ShopMemberPayItemDO;
import cn.iocoder.yudao.module.shop.dal.dataobject.order.ShopOrderItemDO;
import cn.iocoder.yudao.module.shop.dal.dataobject.orderfeedback.ShopOrderFeedbackDO;
import cn.iocoder.yudao.module.shop.dal.dataobject.priceitem.ShopPriceItemDO;
import cn.iocoder.yudao.module.shop.dal.mysql.order.ShopOrderItemMapper;
import cn.iocoder.yudao.module.shop.dal.redis.ShopNoRedisDAO;
import cn.iocoder.yudao.module.shop.service.crossorder.ShopCrossOrderService;
import cn.iocoder.yudao.module.shop.service.crosspriceitem.ShopCrossPriceItemService;
import cn.iocoder.yudao.module.shop.service.item.ShopItemService;
import cn.iocoder.yudao.module.shop.service.itemcount.ShopItemCountService;
import cn.iocoder.yudao.module.shop.service.itemtimes.ShopItemTimesService;
import cn.iocoder.yudao.module.shop.service.masterstatistics.ShopMasterStatisticsService;
import cn.iocoder.yudao.module.shop.service.memberpay.ShopMemberPayItemService;
import cn.iocoder.yudao.module.shop.service.memberpay.ShopMemberPayService;
import cn.iocoder.yudao.module.shop.service.order.handler.ShopOrderHandler;
import cn.iocoder.yudao.module.shop.service.orderfeedback.ShopOrderFeedbackService;
import cn.iocoder.yudao.module.shop.service.paytype.PayTypeService;
import cn.iocoder.yudao.module.shop.service.priceitem.ShopPriceItemService;
import cn.iocoder.yudao.module.shop.service.statistics.ShopStatisticsService;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;

import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.util.*;
import cn.iocoder.yudao.module.shop.controller.admin.order.vo.*;
import cn.iocoder.yudao.module.shop.dal.dataobject.order.ShopOrderDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;

import cn.iocoder.yudao.module.shop.convert.order.ShopOrderConvert;
import cn.iocoder.yudao.module.shop.dal.mysql.order.ShopOrderMapper;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
import static cn.iocoder.yudao.module.shop.enums.ErrorCodeConstants.*;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;

/**
 * 店内消耗订单 Service 实现类
 *
 * @author 庄严伽
 */
@Service
@Validated
public class ShopOrderServiceImpl implements ShopOrderService {

    @Resource
    private ShopOrderMapper orderMapper;

    @Resource
    private ShopOrderItemMapper shopOrderItemMapper;

    @Resource
    private ShopNoRedisDAO shopNoRedisDAO;

    @Resource
    private ShopItemService shopItemService;

    @Resource
    private List<ShopOrderHandler> shopOrderHandlers;

    @Resource
    private ShopMemberPayItemService shopMemberPayItemService;

    @Resource
    private ShopMemberPayService shopmemberpayser;

    @Resource
    private PayTypeService payTypeService;

    @Resource
    private AdminUserApi adminUserApi;

    @Resource
    private MemberUserApi memberUserApi;

    @Resource
    private ShopOrderItemService shopOrderItemService;

    @Resource
    private ShopPriceItemService shopPriceItemService;

    @Resource
    private ShopItemTimesService shopItemTimesService;

    @Resource
    private ShopStatisticsService shopStatisticsService;

    @Resource
    private ShopOrderFeedbackService shopOrderFeedbackService;

    @Resource
    private ShopMasterStatisticsService shopMasterStatisticsService;

    @Resource
    private ShopCrossPriceItemService shopCrossPriceItemService;

    @Resource
    private ShopCrossOrderService shopCrossOrderService;

    @Resource
    private ShopItemCountService shopItemCountService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createOrder(ShopOrderCreateReqVO createReqVO) {
        ShopItemDO shopItemDO = shopItemService.getItem(createReqVO.getShopItemId());
        if(shopItemDO == null){
            //todo
        }

        if(shopItemDO.getLimitCount() > 0){
            ShopItemCountDO itemCountDO = shopItemCountService.getItemCountByItemId(createReqVO.getMemberId(), createReqVO.getShopItemId());
            if(itemCountDO != null){
                if(itemCountDO.getNum() >= shopItemDO.getLimitCount()){
                    throw exception(ORDER_LIMIT_COUNT);
                }
            }else{
                itemCountDO = shopItemCountService.create(createReqVO.getMemberId(), createReqVO.getShopItemId());
            }

            itemCountDO.setNum(itemCountDO.getNum() + 1);
            shopItemCountService.update(itemCountDO);
        }

        ShopOrderDO order = buildShopOrder(createReqVO);

        calcPrice(order, createReqVO);
        calcTime(order, createReqVO);

        List<ShopOrderItemDO> shopOrderItemDOList = buildShopOrderItem(order, createReqVO);
        if(shopOrderItemDOList == null){

        }

        // 2. 订单创建前的逻辑
        shopOrderHandlers.forEach(handler-> handler.beforeOrderCreate(order, shopOrderItemDOList));

        //保存订单
        orderMapper.insert(order);
        shopOrderItemDOList.forEach(orderItem -> orderItem.setOrderId(order.getId()));
        shopOrderItemMapper.insertBatch(shopOrderItemDOList);

        // 返回
        return order.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean check(ShopOrderCheckReqVO checkReqVO){
        validateOrderExists(checkReqVO.getId());

        ShopOrderDO orderObj = orderMapper.selectById(checkReqVO.getId());

        if(orderObj.getPayStatus()){
            throw exception(ORDER_HAD_PAY);
        }

        if(checkReqVO.getWalletCharge() && checkReqVO.getTimesCharge()){
            throw exception(ORDER_CHECK_CHARGE_TYPE_ERROR);
        }

        if(checkReqVO.getTimesCharge()){
            if(checkReqVO.getItemId() == null){
                throw exception(ORDER_CHECK_TIMES_ERROR);
            }

            if(checkReqVO.getTimes() == null){
                throw exception(ORDER_CHECK_TIMES_ERROR);
            }

            ShopItemTimesDO itemTimesDO = shopItemTimesService.getItemTimes(orderObj.getMemberId(), checkReqVO.getItemId());
            if(itemTimesDO == null){
                throw exception(ORDER_CHECK_TIMES_NOT_ENOUGH);
            }

            if(itemTimesDO.getNum() < checkReqVO.getTimes()){
                throw exception(ORDER_CHECK_TIMES_NOT_ENOUGH);
            }

            orderObj.setPayPrice(checkReqVO.getTimes());
            orderObj.setTimesCharge(checkReqVO.getTimesCharge());
            orderObj.setDiscount(false);

            shopItemTimesService.subTimes(orderObj.getMemberId(), checkReqVO.getItemId(), checkReqVO.getTimes(), "店内次数核销", orderObj.getTenantId());
        }else{
            if(checkReqVO.getDiscount()){
                if(checkReqVO.getFinalPayPrice() == null) {
                    throw exception(ORDER_CHECK_FINAL_PRICE);
                } else {
                    orderObj.setPayPrice(checkReqVO.getFinalPayPrice() * 100);
                }
            }

            orderObj.setDiscount(checkReqVO.getDiscount());
            orderObj.setWalletCharge(checkReqVO.getWalletCharge());
            orderMapper.updateById(orderObj);
        }

        // 如果是跨店的，生成记录
        shopCrossOrderService.checkOrder(orderObj);

        List<ShopOrderItemDO> shopOrderItemDOList = shopOrderItemMapper.selectListByOrderId(orderObj.getId());
        shopOrderHandlers.forEach(handler -> handler.afterPayOrder(orderObj, shopOrderItemDOList));

        orderObj.setPayStatus(true);
        orderMapper.updateById(orderObj);

        shopStatisticsService.recordOrder(orderObj);
        ShopOrderFeedbackDO feedbackDO = shopOrderFeedbackService.createByOrder(orderObj, shopOrderItemDOList);
        shopMasterStatisticsService.recordFeedback(feedbackDO);

        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean rollback(Long orderId){
        ShopOrderDO orderDO = orderMapper.selectById(orderId);
        if(orderDO == null) {
            throw exception(ORDER_NOT_EXISTS);
        }

        if(!orderDO.getPayStatus()){
            throw exception(ORDER_NOT_PAY);
        }

        List<ShopOrderItemDO> itemList = shopOrderItemService.getShopOrderItemListByOrderId(orderDO.getId());
        for(int i = 0; i<itemList.size();i++){
            ShopOrderItemDO shopOrderItemDO = itemList.get(i);

            ShopItemDO shopItemDO = shopItemService.getItem(shopOrderItemDO.getOptItemId());
            if(shopItemDO == null){
                //todo
            }

            if(shopItemDO.getLimitCount() > 0){
                ShopItemCountDO itemCountDO = shopItemCountService.getItemCountByItemId(orderDO.getMemberId(), shopOrderItemDO.getOptItemId());
                if(itemCountDO != null){
                    itemCountDO.setNum(itemCountDO.getNum() - 1);
                    shopItemCountService.update(itemCountDO);
                }
            }
        }

        //1余额支付，2次数支付，3现金支付
        if(orderDO.getWalletCharge()){

        }else if(orderDO.getTimesCharge()){
            if(itemList.isEmpty()){
                throw exception(ORDER_ROLLBACK_NOT_FIND_ITEM);
            }

            Long itemId = itemList.get(0).getOptItemId();
            ShopItemTimesDO itemTimesDO = shopItemTimesService.getItemTimes(orderDO.getMemberId(), itemId);
            if(itemTimesDO == null){
                throw exception(ORDER_CHECK_TIMES_NOT_ENOUGH);
            }

            shopItemTimesService.rollbackTimes(orderDO.getMemberId(), itemId, orderDO.getPayPrice(), "店铺订单退回", orderDO.getTenantId());
        }

        shopCrossOrderService.rollback(orderDO);

        List<ShopOrderItemDO> shopOrderItemDOList = shopOrderItemMapper.selectListByOrderId(orderDO.getId());
        shopOrderHandlers.forEach(handler -> handler.rollbackOrder(orderDO, shopOrderItemDOList));

        shopStatisticsService.rollbackOrder(orderDO);
        shopOrderFeedbackService.rollback(orderDO);
        orderMapper.deleteById(orderDO);

        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateOrder(ShopOrderUpdateReqVO updateReqVO) {
        // 校验存在
        validateOrderExists(updateReqVO.getId());
        // 更新
        ShopOrderDO updateObj = ShopOrderConvert.INSTANCE.convert(updateReqVO);
        orderMapper.updateById(updateObj);
    }

    @Override
    public void deleteOrder(Long id) {
        // 校验存在
        validateOrderExists(id);
        // 删除
        orderMapper.deleteById(id);
    }

    @Override
    public void cancelOrder(Long id) {
        validateOrderExists(id);

        ShopOrderDO shopOrderDO = getOrder(id);

        List<ShopOrderItemDO> itemList = shopOrderItemService.getShopOrderItemListByOrderId(id);
        for(int i = 0; i<itemList.size();i++){
            ShopOrderItemDO shopOrderItemDO = itemList.get(i);

            ShopItemDO shopItemDO = shopItemService.getItem(shopOrderItemDO.getOptItemId());
            if(shopItemDO == null){
                //todo
            }

            if(shopItemDO.getLimitCount() > 0){
                ShopItemCountDO itemCountDO = shopItemCountService.getItemCountByItemId(shopOrderDO.getMemberId(), shopOrderItemDO.getOptItemId());
                if(itemCountDO != null){
                    itemCountDO.setNum(itemCountDO.getNum() - 1);
                    shopItemCountService.update(itemCountDO);
                }
            }
        }

        if(shopOrderDO.getPayStatus() == true){
            throw exception(ORDER_CANCEL_HAD_PAY);
        }
        orderMapper.deleteById(id);
    }

    private void validateOrderExists(Long id) {
        if (orderMapper.selectById(id) == null) {
            throw exception(ORDER_NOT_EXISTS);
        }
    }

    @Override
    public ShopOrderDO getOrder(Long id) {
        return orderMapper.selectById(id);
    }

    @Override
    public List<ShopOrderDO> getOrderList(Collection<Long> ids) {
        if (CollUtil.isEmpty(ids)) {
            return ListUtil.empty();
        }
        return orderMapper.selectBatchIds(ids);
    }

    @Override
    public List<ShopOrderDO> getNoCheckOrderList(LocalDateTime beforeTime, Long tenantId){
        return orderMapper.selectNoCheckOrderList(beforeTime, tenantId);
    }

    @Override
    public PageResult<ShopOrderDO> getOrderPage(ShopOrderPageReqVO pageReqVO) {
        return orderMapper.selectPage(pageReqVO);
    }

    @Override
    public List<ShopOrderDO> getOrderList(ShopOrderExportReqVO exportReqVO) {
        return orderMapper.selectList(exportReqVO);
    }

    @Override
    public List<ShopOrderRespVO> getOrderList(LocalDateTime dateTime, Long tenantId) {
        LocalDateTime beginTime = dateTime.withHour(0).withMinute(0).withSecond(0);
        LocalDateTime endTime = dateTime.withHour(23).withMinute(59).withSecond(59);

        List<ShopOrderDO> shopOrderDOList = orderMapper.selectList(new LambdaQueryWrapperX<ShopOrderDO>()
                .betweenIfPresent(ShopOrderDO::getStartTime, beginTime, endTime)
                .eq(ShopOrderDO::getTenantId, tenantId));

        // 查询艾灸师
        Set<Long> userIds = convertSet(shopOrderDOList, ShopOrderDO::getUserId);
        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(userIds);

        // 查询用户信息
        Set<Long> memberIds = convertSet(shopOrderDOList, ShopOrderDO::getMemberId);
        Map<Long, MemberUserRespDTO> memberMap = memberUserApi.getUserMap(memberIds);

        List<ShopOrderItemDO> shopOrderItemDOS = shopOrderItemService.getShopOrderItemListByOrderId(convertSet(shopOrderDOList, ShopOrderDO::getId));

        //查询店内项目
        Set<Long> itemIds = convertSet(shopOrderItemDOS, ShopOrderItemDO::getOptItemId);
        Map<Long, ShopItemRespVO> shopItemRespVOMap = shopItemService.getItemMap(itemIds);

        return ShopOrderConvert.INSTANCE.convertList(shopOrderDOList, shopOrderItemDOS, shopItemRespVOMap, memberMap, userMap);
    }

    //-----------------------------------------------//

    //创建订单
    private ShopOrderDO buildShopOrder(ShopOrderCreateReqVO createReqVO)
    {
        ShopOrderDO order = ShopOrderConvert.INSTANCE.convert(createReqVO);
        order.setNo(shopNoRedisDAO.generate(ShopNoRedisDAO.SHOP_ORDER_NO_PREFIX));
        order.setPayStatus(false);
        order.setDiscount(false);
        order.setWalletCharge(false);
        order.setTenantId(createReqVO.getTenantId());

        return order;
    }

    private void calcPrice(ShopOrderDO order, ShopOrderCreateReqVO createReqVO)
    {
        ShopItemDO shopItemDO = shopItemService.getItem(createReqVO.getShopItemId());
        if(shopItemDO == null){
            //todo
        }

        Integer totalPrice = shopItemDO.getPrice() * createReqVO.getCount();
        order.setTotalPrice(totalPrice);


        MemberUserRespDTO member = memberUserApi.getUser(order.getMemberId());

        if(member == null){
            throw exception(ORDER_FIND_MEMBER_ERROR);
        }

        if(!order.getTenantId().equals(member.getTenantId())){
            //跨店了
            ShopMemberPayItemDO shopMemberPayItemDO = shopMemberPayItemService.getMemberPayItemByMemberId(createReqVO.getMemberId());
            if(shopMemberPayItemDO == null){
                throw exception(MEMBER_NOT_PRICE);
            }


            ShopCrossPriceItemDO shopCrossPriceItemDO = shopCrossPriceItemService.getByTenantId(order.getTenantId(), shopItemDO.getId(), member.getTenantId());
            if(shopCrossPriceItemDO == null || shopCrossPriceItemDO.getTargetItemId().equals(-1)){
                throw exception(ORDER_FIND_ITEM_ERROR);
            }

            ShopItemDO crossShopItemDO = shopItemService.getItem(shopCrossPriceItemDO.getTargetItemId());
            if(crossShopItemDO == null){
                throw exception(ORDER_FIND_ITEM_ERROR2);
            }

            ShopPriceItemDO shopPriceItemDO = shopPriceItemService.getPriceItem(shopMemberPayItemDO.getPriceId(), crossShopItemDO.getId());
            if(shopPriceItemDO == null){
                throw exception(PRICE_ITEM_NOT_SETTING);
            }

            Integer payPrice = shopPriceItemDO.getPrice() * createReqVO.getCount();

            order.setPayPrice(payPrice);

        }else{
            ShopMemberPayItemDO shopMemberPayItemDO = shopMemberPayItemService.getMemberPayItemByMemberId(createReqVO.getMemberId());
            if(shopMemberPayItemDO == null){
                throw exception(MEMBER_NOT_PRICE);
            }

            ShopPriceItemDO shopPriceItemDO = shopPriceItemService.getPriceItem(shopMemberPayItemDO.getPriceId(), shopItemDO.getId());
            if(shopPriceItemDO == null){
                throw exception(PRICE_ITEM_NOT_SETTING);
            }

            Integer payPrice = shopPriceItemDO.getPrice() * createReqVO.getCount();

            order.setPayPrice(payPrice);
        }
    }

    private void calcTime(ShopOrderDO order, ShopOrderCreateReqVO createReqVO){
        Integer interval = shop.INTERVAL;

        LocalDateTime workTime = createReqVO.getDateTime();
        workTime = workTime.withHour(shop.HOUR).withMinute(shop.MINUTE).withSecond(0);

        ShopItemDO shopItemDO = shopItemService.getItem(createReqVO.getShopItemId());
        if(shopItemDO == null){
            throw exception(ITEM_NOT_EXISTS);
        }

        LocalDateTime startTime = workTime.plusMinutes(interval * createReqVO.getTimeIndex());
        LocalDateTime endTime = startTime.plusMinutes(shopItemDO.getTime() * createReqVO.getCount());

        order.setStartTime(startTime);
        order.setEndTime(endTime);
    }

    private List<ShopOrderItemDO> buildShopOrderItem(ShopOrderDO order, ShopOrderCreateReqVO createReqVO)
    {
        return ShopOrderConvert.INSTANCE.convertList(order, createReqVO);
    }

    @Override
    public Long getOrderCountByMemberId(Long memberId){
        return orderMapper.selectCount(ShopOrderDO::getMemberId, memberId);
    }
}
